/**
 **********************************************************************************************************************
 * @file    cot_menu.c
 * @brief   该文件提供菜单框架功能
 * @details 实现了一个完整的多级菜单系统：
 *          1. 支持静态和动态内存分配
 *          2. 提供完整的菜单生命周期管理
 *          3. 支持多级菜单嵌套
 *          4. 实现菜单项的选择与导航
 *          5. 提供丰富的回调机制
 *
 * @details  功能详细说明：
 *           + 菜单初始化函数
 *           + 返回主菜单函数
 *           + 菜单控制函数
 *           + 菜单轮询任务函数
 *
 **********************************************************************************************************************
 * 使用方式:
 *    1、使用前初始化函数 cotMenu_Init, 设置主菜单内容
 *    2、周期调用函数 cotMenu_Task, 用来处理菜单显示和执行相关回调函数
 *    3、使用其他函数对菜单界面控制
 *
 **********************************************************************************************************************
 */

/* Includes ----------------------------------------------------------------------------------------------------------*/
#include "cot_menu.h"

#ifdef _COT_MENU_USE_MALLOC_
#include <malloc.h> /* 用于动态内存分配 */
#endif

#ifdef _COT_MENU_USE_SHORTCUT_
#include <stdarg.h> /* 用于支持可变参数功能 */
#endif

/* Private typedef ---------------------------------------------------------------------------------------------------*/
/**
 * @brief 菜单控制器结构体
 * @details 管理单个菜单层级的所有信息，包括：
 *          - 父子菜单关系
 *          - 菜单显示内容
 *          - 菜单回调函数
 *          - 菜单状态信息
 */
typedef struct MenuCtrl
{
    struct MenuCtrl *pParentMenuCtrl;    /* 父菜单控制器指针，用于返回上级菜单 */
    cotMenuDsecStr_u uMenuDesc;          /* 当前菜单的描述文本 */
    void *pExtendData;                   /* 菜单项的扩展数据，用于传递额外信息 */
    cotShowMenuCallFun_f pfnShowMenuFun; /* 菜单显示回调函数，负责具体的显示效果 */
    cotMenuList_t *pMenuList;            /* 子菜单列表，指向当前菜单包含的所有子项 */
    cotMenuCallFun_f pfnLoadCallFun;     /* 菜单加载回调，在菜单显示前调用 */
    cotMenuCallFun_f pfnRunCallFun;      /* 菜单运行回调，用于执行非菜单功能 */
    menusize_t itemsNum;                 /* 子菜单项总数 */
    menusize_t showBaseItem;             /* 当前显示的第一个菜单项索引 */
    menusize_t selectItem;               /* 当前选中的菜单项索引 */
    bool isSelected;                     /* 菜单项选中状态标志 */
} MenuCtrl_t;

/**
 * @brief 菜单管理器结构体
 * @details 管理整个菜单系统的全局状态：
 *          - 当前活动的菜单控制器
 *          - 主菜单的进入/退出回调
 *          - 菜单系统状态标志
 */
typedef struct
{
    MenuCtrl_t *pMenuCtrl;                /* 当前菜单控制处理 */
    cotMenuCallFun_f pfnMainEnterCallFun; /* 主菜单进入时(进入菜单)需要执行一次的函数 */
    cotMenuCallFun_f pfnMainExitCallFun;  /* 主菜单进入后退出时(退出菜单)需要执行一次的函数 */
    cotMenuCallFun_f pfnLoadCallFun;      /* 重加载函数 */
    uint8_t isEnterMainMenu : 1;          /* 是否进入了主菜单 */
} MenuManage_t;

/* Private define ----------------------------------------------------------------------------------------------------*/
/* Private macro -----------------------------------------------------------------------------------------------------*/
/* Private variables -------------------------------------------------------------------------------------------------*/
static MenuManage_t sg_tMenuManage; /* 全局菜单管理器实例 */

#ifndef _COT_MENU_USE_MALLOC_
static MenuCtrl_t sg_arrMenuCtrl[COT_MENU_MAX_DEPTH]; /* 静态菜单控制器数组 */
#endif

static uint8_t sg_currMenuDepth = 0; /* 当前菜单深度计数器 */

/* Private function prototypes ---------------------------------------------------------------------------------------*/
static MenuCtrl_t *NewMenu(void);
static void DeleteMenu(MenuCtrl_t *pMenu);

/* Private function --------------------------------------------------------------------------------------------------*/
/**
 * @brief      新建菜单层级
 * @details    创建新的菜单控制器，检查深度限制并根据编译配置决定使用动态或静态内存分配
 *
 * @retval     MenuCtrl_t* 返回新创建的菜单控制器指针，失败返回NULL
 */
static MenuCtrl_t *NewMenu(void)
{
    MenuCtrl_t *pMenuCtrl = NULL; /* 菜单控制器指针 */

    /* 检查菜单深度是否达到最大限制，防止无限嵌套 */
    if (sg_currMenuDepth < COT_MENU_MAX_DEPTH)
    {
#ifdef _COT_MENU_USE_MALLOC_
        /* 使用动态内存分配创建菜单控制器 */
        pMenuCtrl = (MenuCtrl_t *)malloc(sizeof(MenuCtrl_t)); /* 分配内存 */
#else
        /* 使用静态数组存储菜单控制器，避免动态分配 */
        pMenuCtrl = &sg_arrMenuCtrl[sg_currMenuDepth]; /* 使用深度作为索引 */
#endif
        /* 创建成功后增加菜单深度计数器 */
        if (pMenuCtrl != NULL)
        {
            sg_currMenuDepth++; /* 确保只有创建成功才增加深度计数 */
        }
    }

    /* 返回创建的菜单控制器指针，如深度超限则返回NULL */
    return pMenuCtrl;
}

/**
 * @brief      销毁菜单层级
 * @details    根据编译配置释放菜单控制器内存（动态分配模式下）并更新菜单深度计数
 *
 * @param[in]  pMenu 要销毁的菜单控制器指针
 * @note       该函数会根据 _COT_MENU_USE_MALLOC_ 的定义决定是否释放内存
 * @retval     None
 */
static void DeleteMenu(MenuCtrl_t *pMenu)
{
#ifdef _COT_MENU_USE_MALLOC_
    /* 在动态内存分配模式下释放菜单控制器占用的内存 */
    if (pMenu != NULL)
    {
        free(pMenu); /* 释放动态分配的内存 */
    }
#endif
    /* 减少菜单深度计数，确保不会出现负值 */
    if (sg_currMenuDepth > 0)
    {
        sg_currMenuDepth--; /* 每释放一个菜单减少一层深度 */
    }
}

/**
 * @brief      菜单初始化
 * @details    创建主菜单控制器，配置菜单参数和回调函数，初始化菜单管理器状态
 *
 * @param[in]  pMainMenu 主菜单配置信息，包含描述文本、回调函数等
 * @retval     0 初始化成功
 * @retval     -1 初始化失败
 */
int cotMenu_Init(cotMainMenuCfg_t *pMainMenu)
{
    MenuCtrl_t *pNewMenuCtrl = NULL;

    /* 检查菜单系统是否已经初始化，避免重复初始化 */
    if (sg_tMenuManage.pMenuCtrl != NULL)
    {
        return -1;
    }

    /* 重置菜单深度计数器（如果启用了深度限制） */
#if COT_MENU_MAX_DEPTH != 0
    sg_currMenuDepth = 0;
#endif

    /* 创建主菜单控制器 */
    if ((pNewMenuCtrl = NewMenu()) == NULL)
    {
        return -1;
    }

    /* 初始化主菜单控制器的基本属性 */
    pNewMenuCtrl->uMenuDesc = pMainMenu->uMenuDesc;           /* 设置菜单描述文本 */
    pNewMenuCtrl->pExtendData = pMainMenu->pExtendData;       /* 设置扩展数据 */
    pNewMenuCtrl->pParentMenuCtrl = NULL;                     /* 主菜单没有父菜单 */
    pNewMenuCtrl->pfnLoadCallFun = pMainMenu->pfnLoadCallFun; /* 设置加载回调 */
    pNewMenuCtrl->pfnShowMenuFun = NULL;                      /* 显示函数初始为空 */
    pNewMenuCtrl->pfnRunCallFun = pMainMenu->pfnRunCallFun;   /* 设置运行回调 */

    /* 初始化菜单列表相关属性 */
    pNewMenuCtrl->pMenuList = NULL;   /* 菜单列表初始为空 */
    pNewMenuCtrl->itemsNum = 0;       /* 菜单项数量初始为0 */
    pNewMenuCtrl->selectItem = 0;     /* 选中项初始为第一项 */
    pNewMenuCtrl->showBaseItem = 0;   /* 显示基准项初始为第一项 */
    pNewMenuCtrl->isSelected = false; /* 选中项初始为未选中 */

    /* 初始化全局菜单管理器 */
    sg_tMenuManage.pMenuCtrl = pNewMenuCtrl;                         /* 设置当前菜单控制器 */
    sg_tMenuManage.isEnterMainMenu = 0;                              /* 初始未进入主菜单 */
    sg_tMenuManage.pfnMainEnterCallFun = pMainMenu->pfnEnterCallFun; /* 设置主菜单进入回调 */
    sg_tMenuManage.pfnMainExitCallFun = pMainMenu->pfnExitCallFun;   /* 设置主菜单退出回调 */
    sg_tMenuManage.pfnLoadCallFun = pNewMenuCtrl->pfnLoadCallFun;    /* 设置加载回调 */

    return 0;
}

/**
 * @brief      菜单反初始化
 * @details    退出所有菜单层级，释放资源并重置菜单系统状态和回调函数
 *
 * @attention  不管处于任何界面都会逐级退出到主菜单后（会调用退出函数），再退出主菜单，最后反初始化
 * @note       反初始化后需要重新调用 cotMenu_Init 才能使用菜单功能
 * @warning    该函数会触发所有未执行的退出回调
 * @retval     0 成功
 * @retval     -1 失败
 */
int cotMenu_DeInit(void)
{
    /* 检查菜单系统状态和有效性，确保已初始化 */
    if (sg_tMenuManage.pMenuCtrl == NULL)
    {
        return -1; /* 菜单系统未初始化，无需反初始化 */
    }

    /* 逐级退出到主菜单，并执行所有必要的退出回调函数 */
    cotMenu_MainExit(); /* 确保所有子菜单都被正确退出，并触发相应回调 */

    /* 释放主菜单控制器内存并清空指针 */
    DeleteMenu(sg_tMenuManage.pMenuCtrl); /* 释放主菜单控制器占用的内存 */
    sg_tMenuManage.pMenuCtrl = NULL;      /* 避免悬空指针，防止后续误用 */

    /* 重置菜单系统状态标志 */
    sg_tMenuManage.isEnterMainMenu = 0; /* 标记为未进入主菜单状态 */

    /* 清空所有回调函数指针，防止后续误调用 */
    sg_tMenuManage.pfnMainEnterCallFun = NULL; /* 清空主菜单进入回调 */
    sg_tMenuManage.pfnMainExitCallFun = NULL;  /* 清空主菜单退出回调 */
    sg_tMenuManage.pfnLoadCallFun = NULL;      /* 清空加载回调 */

    return 0; /* 反初始化成功 */
}

/**
 * @brief      子菜单绑定当前菜单选项
 * @details    将菜单列表和显示函数绑定到当前菜单控制器，如果已绑定则不进行操作
 *
 * @param[in]  pMenuList 要绑定的菜单列表
 * @param[in]  menuNum 菜单列表中的项目数量
 * @param[in]  pfnShowMenuFun 菜单显示回调函数
 * @retval     0 绑定成功
 * @retval     -1 绑定失败
 */
int cotMenu_Bind(cotMenuList_t *pMenuList, menusize_t menuNum, cotShowMenuCallFun_f pfnShowMenuFun)
{
    /* 检查菜单控制器是否有效，确保菜单系统已初始化 */
    if (sg_tMenuManage.pMenuCtrl == NULL)
    {
        return -1; /* 菜单系统未初始化，无法绑定 */
    }

    /* 检查是否已经绑定过菜单列表，避免重复绑定 */
    if (sg_tMenuManage.pMenuCtrl->pMenuList != NULL)
    {
        return 0; /* 已经绑定过菜单列表，返回成功但不做操作 */
    }

    /* 设置菜单列表指针和项目数量 */
    sg_tMenuManage.pMenuCtrl->pMenuList = pMenuList; /* 关联菜单列表 */
    sg_tMenuManage.pMenuCtrl->itemsNum = menuNum;    /* 设置菜单项数量 */

    /* 如果提供了显示回调函数，则更新显示函数指针 */
    if (pfnShowMenuFun != NULL)
    {
        sg_tMenuManage.pMenuCtrl->pfnShowMenuFun = pfnShowMenuFun; /* 设置菜单显示函数 */
    }

    return 0; /* 绑定成功 */
}

/**
 * @brief      复位菜单到主菜单界面
 * @details    释放所有子菜单控制器，返回到主菜单层级并重置选择项
 *
 * @note       该复位操作不会执行任何退出回调函数
 * @warning    该函数会直接释放所有子菜单，不保存状态
 * @retval     0 成功
 * @retval     -1 失败
 */
int cotMenu_Reset(void)
{
    /* 检查菜单系统状态和有效性，确保已初始化并进入了主菜单 */
    if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.isEnterMainMenu == 0)
    {
        return -1; /* 菜单未初始化或未进入主菜单状态 */
    }

    /* 循环释放所有子菜单，直到回到主菜单 */
    while (sg_tMenuManage.pMenuCtrl->pParentMenuCtrl != NULL)
    {
        /* 保存当前菜单控制器指针，用于后续释放 */
        MenuCtrl_t *pMenuCtrl = sg_tMenuManage.pMenuCtrl;

        /* 切换到父菜单，建立父子关系 */
        sg_tMenuManage.pMenuCtrl = sg_tMenuManage.pMenuCtrl->pParentMenuCtrl;

        /* 释放当前菜单控制器的内存 */
        DeleteMenu(pMenuCtrl);
    }

    /* 重置主菜单选择项为第一项，恢复初始状态 */
    sg_tMenuManage.pMenuCtrl->selectItem = 0;

    return 0; /* 复位成功 */
}

/**
 * @brief      主菜单进入
 * @details    执行主菜单进入回调函数，设置主菜单进入标志和加载函数指针
 *
 * @note       必须在 cotMenu_Init 初始化之后调用
 * @warning    重复调用该函数会返回失败
 * @retval     0 成功进入主菜单
 * @retval     -1 失败
 */
int cotMenu_MainEnter(void)
{
    /* 检查菜单系统状态和有效性，确保已初始化但未进入主菜单 */
    if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.isEnterMainMenu == 1)
    {
        return -1; /* 菜单未初始化或已经进入主菜单状态 */
    }

    /* 执行主菜单进入回调函数（如果存在） */
    if (sg_tMenuManage.pfnMainEnterCallFun != NULL)
    {
        cotMenuItemInfo_t tItemInfo;

        /* 准备回调函数参数：主菜单的描述文本和扩展数据 */
        tItemInfo.uMenuDesc = sg_tMenuManage.pMenuCtrl->uMenuDesc;
        tItemInfo.pExtendData = sg_tMenuManage.pMenuCtrl->pExtendData;

        /* 执行主菜单进入回调，通知外部系统菜单已进入 */
        sg_tMenuManage.pfnMainEnterCallFun(&tItemInfo);
    }

    /* 更新菜单系统状态标志，标记为已进入主菜单 */
    sg_tMenuManage.isEnterMainMenu = 1;

    /* 设置加载函数指针，用于菜单首次显示 */
    sg_tMenuManage.pfnLoadCallFun = sg_tMenuManage.pMenuCtrl->pfnLoadCallFun;

    return 0; /* 进入主菜单成功 */
}

/**
 * @brief      主菜单退出
 * @details    逐级退出所有子菜单，执行主菜单退出回调函数，清除主菜单进入标志
 *
 * @attention  不管处于任何界面都会逐级退出到主菜单后（会调用退出函数），再退出主菜单
 * @warning    该函数会触发所有未执行的退出回调函数
 * @retval     0 成功
 * @retval     -1 失败
 */
int cotMenu_MainExit(void)
{
    /* 检查菜单系统状态和有效性，确保已初始化并处于主菜单状态 */
    if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.isEnterMainMenu == 0)
    {
        return -1; /* 菜单未初始化或未进入主菜单状态 */
    }

    /* 逐级退出到主菜单，处理所有子菜单的退出逻辑 */
    while (cotMenu_Exit(1) == 0)
    {
        /* 持续调用 Exit 直到返回错误（表示已经退回主菜单） */
        /* 参数 1 表示每次退出后将子菜单选择项重置为第一项 */
    }

    /* 执行主菜单退出回调函数（如果存在） */
    if (sg_tMenuManage.pfnMainExitCallFun != NULL)
    {
        /* 传递 NULL 作为参数，因为主菜单退出时不需要特定菜单项信息 */
        sg_tMenuManage.pfnMainExitCallFun(NULL);
    }

    /* 更新菜单系统状态，标记为已退出主菜单 */
    sg_tMenuManage.isEnterMainMenu = 0;

    return 0; /* 退出主菜单成功 */
}

/**
 * @brief      进入当前菜单选项
 * @details    创建新菜单控制器，继承父菜单属性并建立父子关联，执行进入回调函数
 *
 * @note       该函数用于进入子菜单或执行菜单项功能
 * @warning    进入新菜单前会创建新的菜单控制器，注意内存使用
 * @retval     0 成功
 * @retval     -1 失败
 */
int cotMenu_Enter(void)
{
    /* 声明变量，存储新旧菜单控制器指针 */
    MenuCtrl_t *pNewMenuCtrl = NULL;                      /* 用于创建新的菜单控制器 */
    MenuCtrl_t *pCurrMenuCtrl = sg_tMenuManage.pMenuCtrl; /* 当前菜单控制器 */

    /* 检查菜单系统状态和有效性，确保已初始化、有菜单列表且处于主菜单状态 */
    if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.pMenuCtrl->pMenuList == NULL || sg_tMenuManage.isEnterMainMenu == 0)
    {
        return -1; /* 菜单未初始化、无菜单列表或未进入主菜单状态 */
    }

    /* 创建新的菜单控制器，用于进入子菜单 */
    if ((pNewMenuCtrl = NewMenu()) == NULL)
    {
        return -1; /* 菜单创建失败，可能是达到了最大深度限制 */
    }

    /* 初始化新菜单控制器的各项属性 */
    /* 从当前选中的菜单项复制描述文本和扩展数据 */
    pNewMenuCtrl->uMenuDesc = pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].uMenuDesc;
    pNewMenuCtrl->pExtendData = pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].pExtendData;

    /* 初始化子菜单列表为空，等待后续绑定 */
    pNewMenuCtrl->pMenuList = NULL;
    pNewMenuCtrl->itemsNum = 0;

    /* 继承父菜单的显示函数，保持显示风格一致 */
    pNewMenuCtrl->pfnShowMenuFun = pCurrMenuCtrl->pfnShowMenuFun;

    /* 从选中的菜单项获取加载和运行回调函数 */
    pNewMenuCtrl->pfnLoadCallFun = pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].pfnLoadCallFun;
    pNewMenuCtrl->pfnRunCallFun = pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].pfnRunCallFun;

    /* 初始化选中项为第一项且设置为已选中状态 */
    pNewMenuCtrl->selectItem = 0;    /* 子菜单初始选中第一项 */
    pNewMenuCtrl->isSelected = true; /* 标记为已选中状态 */

    /* 建立父子菜单关系，指向父菜单控制器，方便返回上一级 */
    pNewMenuCtrl->pParentMenuCtrl = pCurrMenuCtrl;

    /* 更新菜单管理器，将当前菜单指针指向新创建的菜单 */
    sg_tMenuManage.pMenuCtrl = pNewMenuCtrl;
    sg_tMenuManage.pfnLoadCallFun = pNewMenuCtrl->pfnLoadCallFun; /* 更新加载函数指针 */

    /* 如果存在进入回调函数，则执行该回调 */
    if (pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].pfnEnterCallFun != NULL)
    {
        cotMenuItemInfo_t tItemInfo;

        /* 准备回调函数的参数：新菜单的描述和扩展数据 */
        tItemInfo.uMenuDesc = sg_tMenuManage.pMenuCtrl->uMenuDesc;
        tItemInfo.pExtendData = sg_tMenuManage.pMenuCtrl->pExtendData;

        /* 执行菜单项的进入回调函数，通知外部进入了新菜单 */
        pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].pfnEnterCallFun(&tItemInfo);
    }

    return 0; /* 成功进入子菜单 */
}

/**
 * @brief      退出当前选项并返回上一层菜单
 * @details    返回到父菜单控制器，执行退出回调函数，并可选择是否重置选择项
 *
 * @param[in]  isReset 菜单选项是否从头选择
 *             @arg    true 重置为第一项
 *             @arg    false 保持当前选择
 * @note       该函数在主菜单时无法退出
 * @warning    退出时会释放当前菜单控制器的内存
 * @retval     0 成功
 * @retval     -1 失败，即目前处于主菜单，无法返回
 */
int cotMenu_Exit(bool isReset)
{
    /* 保存当前菜单控制器的引用，用于后续释放 */
    MenuCtrl_t *pMenuCtrl = sg_tMenuManage.pMenuCtrl;

    /* 检查菜单系统状态和有效性，确保已初始化且处于主菜单状态 */
    if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.isEnterMainMenu == 0)
    {
        return -1; /* 菜单未初始化或未进入主菜单状态 */
    }

    /* 检查是否已经在主菜单，主菜单无法退出 */
    if (sg_tMenuManage.pMenuCtrl->pParentMenuCtrl == NULL)
    {
        return -1; /* 已经在主菜单，无法返回上一级 */
    }

    /* 切换到父菜单并更新菜单管理器状态 */
    sg_tMenuManage.pMenuCtrl = sg_tMenuManage.pMenuCtrl->pParentMenuCtrl;     /* 回到父菜单 */
    sg_tMenuManage.pfnLoadCallFun = sg_tMenuManage.pMenuCtrl->pfnLoadCallFun; /* 恢复父菜单的加载函数 */

    /* 释放当前菜单控制器的内存，防止内存泄漏 */
    DeleteMenu(pMenuCtrl);
    pMenuCtrl = NULL;

    /* 执行菜单项退出回调函数（如果存在） */
    if (sg_tMenuManage.pMenuCtrl->pMenuList[sg_tMenuManage.pMenuCtrl->selectItem].pfnExitCallFun != NULL)
    {
        cotMenuItemInfo_t tItemInfo;

        /* 准备回调函数的参数：菜单描述和扩展数据 */
        tItemInfo.uMenuDesc = sg_tMenuManage.pMenuCtrl->pMenuList[sg_tMenuManage.pMenuCtrl->selectItem].uMenuDesc;
        tItemInfo.pExtendData = sg_tMenuManage.pMenuCtrl->pMenuList[sg_tMenuManage.pMenuCtrl->selectItem].pExtendData;

        /* 更新父菜单的选中状态为未选中，准备显示菜单列表 */
        sg_tMenuManage.pMenuCtrl->isSelected = false;

        /* 执行退出回调函数，通知外部已退出子菜单 */
        sg_tMenuManage.pMenuCtrl->pMenuList[sg_tMenuManage.pMenuCtrl->selectItem].pfnExitCallFun(&tItemInfo);
    }

    /* 根据传入参数决定是否重置选择项为第一项 */
    if (isReset)
    {
        sg_tMenuManage.pMenuCtrl->selectItem = 0; /* 重置为第一项 */
    }

    return 0; /* 成功退出当前菜单 */
}

/**
 * @brief      选择上一个菜单选项
 * @details    选择前一个菜单项，并根据循环标志决定在第一项时的行为
 *
 * @param[in]  isAllowRoll 第一个选项时是否允许循环跳转到最后一个选项
 *             @arg        true 允许循环跳转到最后一个选项
 *             @arg        false 不允许循环，保持在第一个选项
 * @note       当不允许循环且当前在第一个选项时，选择会失败
 * @warning    在第一个选项时的行为由 isAllowRoll 参数决定
 * @retval     0 成功
 * @retval     -1 失败
 */
int cotMenu_SelectPrevious(bool isAllowRoll)
{
    /* 检查菜单系统状态和有效性，确保菜单系统已初始化、有菜单列表且处于主菜单状态 */
    if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.pMenuCtrl->pMenuList == NULL || sg_tMenuManage.isEnterMainMenu == 0)
    {
        return -1; /* 菜单未初始化、无菜单列表或未进入主菜单状态 */
    }

    /* 如果当前不是第一个选项，直接选择上一个 */
    if (sg_tMenuManage.pMenuCtrl->selectItem > 0)
    {
        /* 选择索引减1，移动到前一项 */
        sg_tMenuManage.pMenuCtrl->selectItem--;
    }
    else
    {
        /* 当前已是第一项，处理循环滚动逻辑 */
        if (isAllowRoll)
        {
            /* 允许循环时，从第一项跳转到最后一项 */
            sg_tMenuManage.pMenuCtrl->selectItem = sg_tMenuManage.pMenuCtrl->itemsNum - 1;
        }
        else
        {
            /* 不允许循环时，保持在第一个选项并返回失败 */
            sg_tMenuManage.pMenuCtrl->selectItem = 0;
            return -1; /* 已经是第一项且不允许循环 */
        }
    }

    return 0; /* 选择成功 */
}

/**
 * @brief      选择下一个菜单选项
 * @details    选择下一个菜单项，并根据循环标志决定在最后一项时的行为
 *
 * @param[in]  isAllowRoll 最后一个选项时是否允许循环跳转到第一个选项
 *             @arg        true 允许循环跳转到第一个选项
 *             @arg        false 不允许循环，保持在最后一个选项
 * @note       当不允许循环且当前在最后一个选项时，选择会失败
 * @warning    在最后一个选项时的行为由 isAllowRoll 参数决定
 * @retval     0 成功
 * @retval     -1 失败
 */
int cotMenu_SelectNext(bool isAllowRoll)
{
    /* 检查菜单系统状态和有效性，确保菜单系统已初始化、有菜单列表且处于主菜单状态 */
    if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.pMenuCtrl->pMenuList == NULL || sg_tMenuManage.isEnterMainMenu == 0)
    {
        return -1; /* 菜单未初始化、无菜单列表或未进入主菜单状态 */
    }

    /* 如果当前不是最后一个选项，直接选择下一个 */
    if (sg_tMenuManage.pMenuCtrl->selectItem < (sg_tMenuManage.pMenuCtrl->itemsNum - 1))
    {
        /* 选择索引加1，移动到下一项 */
        sg_tMenuManage.pMenuCtrl->selectItem++;
    }
    else
    {
        /* 当前已是最后一项，处理循环滚动逻辑 */
        if (isAllowRoll)
        {
            /* 允许循环时，从最后一项跳转到第一项 */
            sg_tMenuManage.pMenuCtrl->selectItem = 0;
        }
        else
        {
            /* 不允许循环时，保持在最后一个选项并返回失败 */
            sg_tMenuManage.pMenuCtrl->selectItem = sg_tMenuManage.pMenuCtrl->itemsNum - 1;
            return -1; /* 已经是最后一项且不允许循环 */
        }
    }

    return 0; /* 选择成功 */
}

/**
 * @brief      选择指定的菜单选项
 * @details    直接设置当前选中项为指定索引，但需确保索引在有效范围内
 *
 * @param[in]  selectItem 指定要选择的菜单项索引（从0开始）
 * @note       该函数不会触发任何回调函数
 * @warning    指定的索引必须小于当前菜单的项目总数
 * @retval     0 成功
 * @retval     -1 失败
 */
int cotMenu_Select(menusize_t selectItem)
{
    /* 检查菜单系统状态和有效性，确保菜单系统已初始化、有菜单列表且处于主菜单状态 */
    if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.pMenuCtrl->pMenuList == NULL || sg_tMenuManage.isEnterMainMenu == 0)
    {
        return -1; /* 菜单未初始化、无菜单列表或未进入主菜单状态 */
    }

    /* 检查索引是否在有效范围内，避免数组越界 */
    if (selectItem >= sg_tMenuManage.pMenuCtrl->itemsNum)
    {
        return -1; /* 指定的索引超出菜单项数量范围 */
    }

    /* 直接更新选中项索引为指定值 */
    sg_tMenuManage.pMenuCtrl->selectItem = selectItem;

    return 0; /* 选择成功 */
}

#ifdef _COT_MENU_USE_SHORTCUT_

/**
 * @brief      快速进入指定的多级菜单选项
 * @details    根据索引序列快速导航至指定的多级菜单，可选择从主菜单或当前菜单开始
 *
 * @param[in]  isAbsolute 是否使用绝对路径（从主菜单开始）
 *             @arg       true 从主菜单开始导航
 *             @arg       false 从当前菜单开始导航
 * @param[in]  deep 要进入的菜单深度，指定后续参数的个数
 * @param[in]  ... 可变参数，各级菜单的索引值（从0开始）
 *                 参数个数必须与 deep 参数值相同
 * @note       该函数需要启用 _COT_MENU_USE_SHORTCUT_ 宏定义
 * @warning    所有索引值必须在各自层级的有效范围内
 * @retval     0 成功
 * @retval     -1 失败
 */
int cotMenu_ShortcutEnter(bool isAbsolute, uint8_t deep, ...)
{
    uint8_t selectDeep = 0; /* 当前已处理的深度计数 */
    va_list pItemList;      /* 可变参数列表 */
    menusize_t selectItem;  /* 当前层级的选择项索引 */

    /* 检查菜单系统状态和有效性 */
    if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.pMenuCtrl->pMenuList == NULL || sg_tMenuManage.isEnterMainMenu == 0)
    {
        return -1;
    }

    /* 如果是绝对路径，先重置到主菜单 */
    if (isAbsolute)
    {
        cotMenu_Reset();
    }

    /* 开始处理可变参数 */
    va_start(pItemList, deep);

    /* 逐级处理每一层菜单 */
    while (selectDeep < deep)
    {
        /* 获取当前层级的选择项索引 */
        selectItem = va_arg(pItemList, int);

        /* 验证索引的有效性 */
        if (selectItem >= sg_tMenuManage.pMenuCtrl->itemsNum)
        {
            va_end(pItemList);
            return -1;
        }

        /* 设置选中项并进入下一级 */
        sg_tMenuManage.pMenuCtrl->selectItem = selectItem;
        cotMenu_Enter();
        selectDeep++;
    }

    /* 清理可变参数列表 */
    va_end(pItemList);

    return 0;
}

#endif

/**
 * @brief      限制当前菜单界面的显示项数量并调整显示基准位置
 * @details    限制菜单显示项数量并调整显示窗口，确保选中项始终在可见范围内
 *
 * @param[in,out]  ptMenuShow   菜单显示信息结构体指针，包含显示基准项等信息
 * @param[in,out]  pShowNum     期望显示的菜单项数量，如超出实际数量将被调整
 * @note       该函数通常在显示回调函数中使用，用于实现滚动显示效果
 * @warning    确保传入的指针参数有效，否则函数将返回失败
 * @retval     0 成功
 * @retval     -1 失败
 */
int cotMenu_LimitShowListNum(cotMenuShow_t *ptMenuShow, menusize_t *pShowNum)
{
    /* 检查参数有效性，避免空指针异常 */
    if (ptMenuShow == NULL || pShowNum == NULL)
    {
        return -1; /* 参数无效，返回失败 */
    }

    /* 限制显示数量不超过实际菜单项总数，防止越界 */
    if (*pShowNum > ptMenuShow->itemsNum)
    {
        *pShowNum = ptMenuShow->itemsNum; /* 调整显示数量为实际菜单项数量 */
    }

    /* 调整显示基准项位置，确保选中项始终可见 */
    if (ptMenuShow->selectItem < ptMenuShow->showBaseItem)
    {
        /* 当选中项在显示窗口之前时，移动窗口到选中项位置 */
        ptMenuShow->showBaseItem = ptMenuShow->selectItem; /* 将显示基准项设置为选中项 */
    }
    else if ((ptMenuShow->selectItem - ptMenuShow->showBaseItem) >= *pShowNum)
    {
        /* 当选中项超出显示窗口时，调整窗口使选中项位于最后一个显示位置 */
        ptMenuShow->showBaseItem = ptMenuShow->selectItem - *pShowNum + 1; /* 调整基准使选中项可见 */
    }
    else
    {
        /* 选中项在显示窗口内，保持当前显示范围不变 */
    }

    return 0; /* 限制显示数量成功 */
}

/**
 * @brief      查询指定层级的父菜单显示信息
 * @details    向上遍历指定层数的父菜单层级，获取并复制菜单信息到提供的结构体中
 *
 * @param[out] ptMenuShow 用于存储父菜单显示信息的结构体指针
 * @param[in]  level      需要获取的父菜单层级数（1表示直接父菜单）
 * @note       level值必须大于0，且不能超过实际的菜单层级深度
 * @warning    确保传入的结构体指针有效，且有足够空间存储菜单项信息
 * @retval     0 成功
 * @retval     -1 失败（参数无效或层级不足）
 */
int cotMenu_QueryParentMenu(cotMenuShow_t *ptMenuShow, uint8_t level)
{
    int i;                        /* 循环计数器，用于复制菜单项数据 */
    cotMenuList_t *pMenu;         /* 菜单列表指针，存储父菜单的子项 */
    MenuCtrl_t *pMenuCtrl = NULL; /* 菜单控制器指针，用于向上遍历父菜单 */

    /* 检查菜单系统状态和有效性，确保已初始化并进入主菜单 */
    if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.isEnterMainMenu == 0)
    {
        return -1; /* 菜单未初始化或未进入主菜单模式 */
    }

    /* 获取当前菜单的父菜单控制器，作为遍历起点 */
    pMenuCtrl = sg_tMenuManage.pMenuCtrl->pParentMenuCtrl;

    /* 逐级向上遍历父菜单，直到达到指定层级或到达最顶层 */
    while (level && pMenuCtrl != NULL)
    {
        /* 获取当前层级的菜单列表，以便复制菜单项数据 */
        pMenu = pMenuCtrl->pMenuList;

        /* 复制菜单基本信息到输出结构体 */
        ptMenuShow->itemsNum = pMenuCtrl->itemsNum;                  /* 菜单项总数 */
        ptMenuShow->selectItem = pMenuCtrl->selectItem;              /* 当前选中项 */
        ptMenuShow->showBaseItem = pMenuCtrl->showBaseItem;          /* 显示基准项 */
        ptMenuShow->uMenuDesc = sg_tMenuManage.pMenuCtrl->uMenuDesc; /* 菜单描述 */

        /* 复制菜单项信息到输出结构体（注意不超过最大项数限制） */
        for (i = 0; i < ptMenuShow->itemsNum && i < COT_MENU_MAX_NUM; i++)
        {
            ptMenuShow->uItemsListDesc[i] = pMenu[i].uMenuDesc;         /* 复制菜单项描述 */
            ptMenuShow->pItemsListExtendData[i] = pMenu[i].pExtendData; /* 复制扩展数据 */
        }

        /* 继续向上一级父菜单遍历，同时递减层级计数 */
        pMenuCtrl = pMenuCtrl->pParentMenuCtrl; /* 移动到上一级菜单 */
        level--;                                /* 减少待处理层级数 */
    }

    /* 检查是否完成了所有层级的遍历，未完成表示层级不足 */
    if (level != 0 && pMenuCtrl == NULL)
    {
        return -1; /* 父菜单层级不足，无法达到请求的层级深度 */
    }

    return 0; /* 查询父菜单信息成功 */
}

/**
 * @brief      菜单任务
 * @details    处理菜单加载回调、显示菜单界面、执行运行时回调，维护菜单系统运行
 *
 * @note       该函数需要周期性调用以维护菜单系统的正常运行
 * @warning    确保调用频率适中，过快会影响性能，过慢会影响响应
 * @retval     0 成功(处于菜单模式)
 * @retval     -1 失败(未处于菜单模式)
 */
int cotMenu_Task(void)
{
    int i;                    /* 循环计数器，用于复制菜单项信息 */
    cotMenuList_t *pMenuList; /* 当前菜单的子菜单列表指针 */
    cotMenuShow_t tMenuShow;  /* 菜单显示信息临时结构体 */

    /* 检查菜单系统状态，确保已初始化并进入了主菜单 */
    if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.isEnterMainMenu == 0)
    {
        return -1; /* 菜单未初始化或未进入主菜单模式 */
    }

    /* 处理一次性加载回调函数（如果存在） */
    if (sg_tMenuManage.pfnLoadCallFun != NULL)
    {
        cotMenuItemInfo_t tItemInfo;

        /* 准备加载回调参数：当前菜单的描述文本和扩展数据 */
        tItemInfo.uMenuDesc = sg_tMenuManage.pMenuCtrl->uMenuDesc;
        tItemInfo.pExtendData = sg_tMenuManage.pMenuCtrl->pExtendData;

        /* 执行加载回调函数 */
        sg_tMenuManage.pfnLoadCallFun(&tItemInfo);

        /* 清除加载回调指针，确保只执行一次 */
        sg_tMenuManage.pfnLoadCallFun = NULL;
    }

    /* 处理菜单显示（仅当当前菜单有子菜单列表时） */
    if (sg_tMenuManage.pMenuCtrl->pMenuList != NULL)
    {
        /* 准备显示数据：复制当前菜单状态到临时显示结构体 */
        pMenuList = sg_tMenuManage.pMenuCtrl->pMenuList;
        tMenuShow.itemsNum = sg_tMenuManage.pMenuCtrl->itemsNum;         /* 菜单项总数 */
        tMenuShow.selectItem = sg_tMenuManage.pMenuCtrl->selectItem;     /* 当前选中项 */
        tMenuShow.showBaseItem = sg_tMenuManage.pMenuCtrl->showBaseItem; /* 显示基准项 */

        /* 复制当前菜单的描述信息 */
        tMenuShow.uMenuDesc = sg_tMenuManage.pMenuCtrl->uMenuDesc;
        tMenuShow.pExtendData = sg_tMenuManage.pMenuCtrl->pExtendData;

        /* 复制所有子菜单项信息到显示缓冲，确保不超过最大项数限制 */
        for (i = 0; i < tMenuShow.itemsNum && i < COT_MENU_MAX_NUM; i++)
        {
            tMenuShow.uItemsListDesc[i] = pMenuList[i].uMenuDesc;
            tMenuShow.pItemsListExtendData[i] = pMenuList[i].pExtendData;
        }

        /* 执行显示回调函数，更新界面显示（如果已设置） */
        if (sg_tMenuManage.pMenuCtrl->pfnShowMenuFun != NULL)
        {
            sg_tMenuManage.pMenuCtrl->pfnShowMenuFun(&tMenuShow);
        }

        /* 保存显示基准项位置，用于滚动显示 */
        sg_tMenuManage.pMenuCtrl->showBaseItem = tMenuShow.showBaseItem;
    }

    /* 执行运行时回调函数（如果存在），用于处理菜单项的周期性任务 */
    if (sg_tMenuManage.pMenuCtrl->pfnRunCallFun != NULL)
    {
        cotMenuItemInfo_t tItemInfo;

        /* 准备运行时回调参数 */
        tItemInfo.uMenuDesc = sg_tMenuManage.pMenuCtrl->uMenuDesc;
        tItemInfo.pExtendData = sg_tMenuManage.pMenuCtrl->pExtendData;

        /* 执行运行时回调函数 */
        sg_tMenuManage.pMenuCtrl->pfnRunCallFun(&tItemInfo);
    }

    return 0; /* 菜单任务执行成功 */
}
